package com.sakura.demo.gateway.authentication;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * @Author: zhengcan
 * @Date: 2022/5/15
 * @Description: 网关token认证管理器：使用redis或jdbc的方式存储token信息时
 *                  注：若使用默认的JwtReactiveAuthenticationManager认证管理器，则本自定义实现Bean无需再注入
 * @Version: 1.0.0 创建
 */
@Slf4j
//@Component
public class GatewayJwtTokenAuthenticationManager implements ReactiveAuthenticationManager {

    @Resource
    private TokenStore jwtTokenStore;

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.justOrEmpty(authentication)
                // 判断是否为BearerTokenAuthenticationToken类型的token
                .filter(token -> token instanceof BearerTokenAuthenticationToken)
                .cast(BearerTokenAuthenticationToken.class)
                // 取出token
                .map(BearerTokenAuthenticationToken::getToken)
                .flatMap(accessToken -> {
                    log.info("accessToken is :{}", accessToken);
                    // 从redis或数据库中取出token
                    OAuth2AccessToken oAuth2AccessToken = this.jwtTokenStore.readAccessToken(accessToken);
                    if (null == oAuth2AccessToken) {
                        return Mono.error(new InvalidTokenException("无效的accessToken！"));
                    } else if (oAuth2AccessToken.isExpired()) {
                        return Mono.error(new InvalidTokenException("accessToken过期失效，请重新获取！"));
                    }
                    // 验证token
                    OAuth2Authentication oAuth2Authentication = this.jwtTokenStore.readAuthentication(accessToken);
                    if (null == oAuth2Authentication) {
                        return Mono.error(new InvalidTokenException("无效的accessToken！"));
                    } else {
                        return Mono.just(oAuth2Authentication);
                    }
                })
                .cast(Authentication.class);

    }
}
